package cn.test.futrue.CompletableFuture.CompletableFuture3;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/** 模拟场景2
 * 一般价格都会进行打折促销，所以我们要在获取价格后获取打折的信息，这些东西还都是从不同国家地方来的，所以还需要查询汇率。
 * 小小分析一波：因为汇率是和价格、折扣是没有关系的，所以汇率可以和价格或者和折扣一块进行查询，折扣是在价格的基础上进行打折，
 * 所以在价格服务调用玩之后才能进行折扣服务。
 *
 * 原文： https://blog.csdn.net/qq_36597450/article/details/81232051  ；
 *
 * public Quote getPrice(String product) {
 * 		delay();
 * 		double price = random.nextDouble() * 100;
 * 		Discount discount = Discount.values()[random.nextInt(Discount.values().length)];
 * 		return new Quote(getName(), price, discount);
 *  } ：：
 * https://github.com/jojozhai/spring-test/blob/1035c98cca3d232ed60c275d120b9f7dab68efdf/bookshop-admin/src/main/java/com/lesson/spring/future/PriceDemo.java
 * https://github.com/MuscleGeeker/bookshop-parent/blob/3205993456ee341c5cdc5afaeffa00d0a7cf0477/bookshop-admin/src/main/java/com/lesson/spring/future/PriceDemo.java
 * 完事
 * future各个基类概念详解：
 * 	https://github.com/hughJack/codeForNothing/blob/a2799c298d959227f4cfa501d6885c5dbce6dc0a/src/main/java/com/ma/juc/example/Future/Demo3_CompleteFuture.java
 * 拓展理解-集思广益：
 *  https://blog.csdn.net/itguangit/article/details/78624404
 *  https://www.cnblogs.com/ten951/p/6590661.html
 */
public class PriceDemoScene2 {
    private List<Shop> shops = new ArrayList<>();

    public static void main(String[] args) {
        PriceDemoScene2 priceDemo = new PriceDemoScene2();
        for (int i = 1; i < 18; i++) {
            priceDemo.shops.add(new Shop("shop" + i));
        }
        Long start = System.currentTimeMillis();
        System.out.println(priceDemo.findPrices12("苹果x"));
//        priceDemo.findPrices20("苹果x");
        System.out.println("服务耗时：" + (System.currentTimeMillis() - start));
    }


    /**进阶2      汇率2
     *  那个查询快就先显示哪个，  先改造一下查询时间，在获取价格和汇率、折扣方法都改成
     *  public static void delay() {
     *         Random random = new Random();
     *        int delay = 500 + random.nextInt(2000);
     *         try {
     *             Thread.sleep(delay);
     *         } catch (Exception e) {
     *             throw new RuntimeException(e);
     *         }
     *   }
     * 这样就达到了我们想要的目的，哪个价格先找出来就显示哪个，不用等程序一块执行完才一块显示.
     * 服务耗时：87
     * shop6 price is 54.098 (done in 2114 msecs)
     * fastest anyOf done null
     * shop2 price is 894.402 (done in 2114 msecs)
     * shop5 price is 478.043 (done in 2114 msecs)
     * shop15 price is 772.284 (done in 2127 msecs)
     * shop4 price is 389.967 (done in 2208 msecs)
     * shop10 price is 729.434 (done in 2259 msecs)
     * shop7 price is 905.589 (done in 2439 msecs)
     * shop14 price is 796.97 (done in 2481 msecs)
     * shop17 price is 117.993 (done in 2483 msecs)
     * shop16 price is 50.942 (done in 2521 msecs)
     * shop11 price is 550.616 (done in 2528 msecs)
     * shop13 price is 106.001 (done in 2601 msecs)
     * shop3 price is 347.486 (done in 2602 msecs)
     * shop12 price is 381.242 (done in 2758 msecs)
     * shop1 price is 809.62 (done in 2868 msecs)
     * shop8 price is 766.232 (done in 2890 msecs)
     * shop9 price is 779.406 (done in 3534 msecs)
     *  all done
     */
    //findPrices12 --> findPrices20   进阶2      汇率2      模拟场景3：2秒（实际上可能更多），对于客户体验来说，应该算是差的，我接受不了淘宝2秒才给我查出我想要的。
    public void findPrices20(String product) {
        long start = System.currentTimeMillis();
        Executor executor = Executors.newCachedThreadPool();
        // Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(),100))

        //CompletableFuture<?> priceFuture =
        CompletableFuture[] priceFuture = shops.stream()
                //这返回的是异步处理
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)
                    .thenCombine(
                        CompletableFuture.supplyAsync(() -> ExchangeDemo.getRate("USD", "CNY"), executor),
//                      (Quote quote, Double rate) -> new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())
                        (quote, rate) -> new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())

//                        new BiFunction<Quote, Double, Quote>() {
//                            @Override
//                            public Quote apply(Quote quote, Double rate) {
//                                return new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount());
//                            }
//                        }

                                )
                )

                //.map(future->future.thenApply(Quote::parse))//thenApp是前一个对象完成了之后调下个对象的方法（parse）
                //thenCompose是当前对象准备扔到一个异步操作里面
                .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> DiscountDemo.applyDiscount(quote), executor)))

                //thenAccept()定义CompletableFuture返回的结果
                .map(future -> future.thenAccept(content -> System.out.println(content + " (done in " + (System.currentTimeMillis() - start) + " msecs)")))
                .toArray(size -> new CompletableFuture[size]);

        CompletableFuture.allOf(priceFuture).thenAccept((obj) ->System.out.println(" all done"));
        //allOf接收一个数组，当里面的CompletableFuture有一个完成时，就会执行下一个语句
        CompletableFuture.anyOf(priceFuture).thenAccept((obj) -> System.out.println("fastest anyOf done " + obj));

    }


    //findPrices12 --> findPrices13     模拟场景2   lamada 还原为 --->  anonymous class
    public List<String> findPrices13(String product) {
        Executor executor = Executors.newCachedThreadPool();
        // Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(),100))

        List<CompletableFuture<String>> priceFuture = shops.stream()
            //这返回的是异步处理
            .map(
                new Function<Shop, CompletableFuture<Quote>>() {
                    @Override
                    public CompletableFuture<Quote> apply(Shop shop) {
                        return CompletableFuture.supplyAsync(
                                new Supplier<Quote>() {
                                    @Override
                                    public Quote get() {
                                        return shop.getPrice(product);
                                    }
                                },
                                executor
                        )
                        .thenCombine(
                                CompletableFuture.supplyAsync(new Supplier<Double>() {
                                    @Override
                                    public Double get() {
                                        return ExchangeDemo.getRate("USD", "CNY");
                                    }
                                }, executor),
                                new BiFunction<Quote, Double, Quote>() {
                                    @Override
                                    public Quote apply(Quote quote, Double rate) {
                                        return new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount());
                                    }
                                }
                        );
                    }
                }
            )

            //.map(future->future.thenApply(Quote::parse))//thenApp是前一个对象完成了之后调下个对象的方法（parse）
            //thenCompose是当前对象准备扔到一个异步操作里面
            .map(new Function<CompletableFuture<Quote>, CompletableFuture<String>>() {
                @Override
                public CompletableFuture<String> apply(CompletableFuture<Quote> future) {
                    return future.thenCompose(new Function<Quote, CompletionStage<String>>() {
                        @Override
                        public CompletionStage<String> apply(Quote quote) {
                            return CompletableFuture.supplyAsync(new Supplier<String>() {
                                @Override
                                public String get() {
                                    return DiscountDemo.applyDiscount(quote);
                                }
                            }, executor);
                        }
                    });
                }
            })

            .collect(Collectors.toList());

		return priceFuture.stream().map(new Function<CompletableFuture<String>, String>() {
            @Override
            public String apply(CompletableFuture<String> stringCompletableFuture) {
                return stringCompletableFuture.join();
            }
        }).collect(Collectors.toList());
    }
    //findPrices11 --> findPrices12     模拟场景2
    public List<String> findPrices12(String product) {
        Executor executor = Executors.newCachedThreadPool();
        // Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(),100))

        List<CompletableFuture<String>> priceFuture = shops.stream()
            //这返回的是异步处理
            .map(
                shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)
                .thenCombine(
                    CompletableFuture.supplyAsync(() -> ExchangeDemo.getRate("USD", "CNY"), executor),
//                  (Quote quote, Double rate) -> new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())
                    (quote, rate) ->              new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())

//                  new BiFunction<Quote, Double, Quote>() {
//                      @Override
//                      public Quote apply(Quote quote, Double rate) {
//                          return new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount());
//                      }
//                  }

                )
            )

            //.map(future->future.thenApply(Quote::parse))//thenApp是前一个对象完成了之后调下个对象的方法（parse）
            //thenCompose是当前对象准备扔到一个异步操作里面
            .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> DiscountDemo.applyDiscount(quote), executor)))

//				.map(future -> future.thenAccept(content ->
//				System.out.println(content + " (done in " + (System.currentTimeMillis() - start )+" msecs)")))
//                .toArray(size -> new CompletableFuture[size]);
            .collect(Collectors.toList());

//        CompletableFuture.anyOf(priceFuture).thenAccept((obj) -> System.out.println("fastest done" + obj));

		return priceFuture.stream().map(CompletableFuture::join).collect(Collectors.toList());
    }

    //由于原文中没有给出Quote类, github上搜出的source
    //https://github.com/jojozhai/spring-test/blob/1035c98cca3d232ed60c275d120b9f7dab68efdf/bookshop-admin/src/main/java/com/lesson/spring/future/PriceDemo.java
    public void findPrices11(String product) {
         Executor executor = Executors.newFixedThreadPool(100);

        CompletableFuture<?>[] priceFuture = shops.stream()
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)
                        .thenCombine(
                            CompletableFuture.supplyAsync(() -> ExchangeDemo.getRate("USD", "CNY"), executor),
//                          (Quote quote, Double rate) -> new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())
//                          (quote, rate) ->              new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount())

                            new BiFunction<Quote, Double, Quote>() {
                                @Override
                                public Quote apply(Quote quote, Double rate) {
                                    return new Quote(quote.getShop(), quote.getPrice() * rate, quote.getDiscount());
                                }
                            }

                        )
                )
                .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> DiscountDemo.applyDiscount(quote), executor)))

//				.map(future -> future.thenAccept(content ->
//				System.out.println(content + " (done in " + (System.currentTimeMillis() - start )+" msecs)")))
                .toArray(size -> new CompletableFuture[size]);
//				.collect(Collectors.toList());

        CompletableFuture.anyOf(priceFuture).thenAccept((obj) -> System.out.println("fastest done" + obj));

//		return priceFuture.stream().map(CompletableFuture::join).collect(Collectors.toList());

    }



}
